package callBack.impl;

import app.FileMeta;
import callBack.FileScannerCallBack;
import com.sun.xml.internal.bind.v2.runtime.output.FastInfosetStreamWriterOutput;
import task.FileScanner;
import util.DBUtil;
import util.PinyinUtil;

import java.io.File;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;

/**
 * 文件信息保存到数据库的回调子类
 */
public class FileSave2DB implements FileScannerCallBack {
    //当一个文件夹的信息发生改变之后，对比视图1和视图2就可以发现
    // 视图1存在视图2不存在的信息就是需要插入的新内容，视图2存在但是视图1不存在的信息就是过期数据，需要在数据库中删除
    @Override
    public void callback(File dir) {
        //列举出当前dir下的所有文件对象
        File[] files=dir.listFiles();
        //若files==null || files。length == 0  就说明该文件夹下就没有文件或者dir就不是个文件夹
        //边界条件
        if(files!=null && files.length!=0) {
            //1、先将当前dir下的所有文件信息保存到内存中,缓存的信息一定是从系统中读取到的最新的数据-视图1
            List<FileMeta> locals = new ArrayList<>();
            for (File file : files) {
                FileMeta meta = new FileMeta();
                if (file.isDirectory()) {
                    //此处遇到的是文件夹，注意此处的路径存储的是父路径
                    setCommonFiled(file.getName(), file.getParent(), true, file.lastModified(), meta);
                } else {
                    //此处遇到的是文件，注意此处的路径存储的是父路径
                    setCommonFiled(file.getName(), file.getParent(), false, file.lastModified(), meta);
                    meta.setSize(file.length());
                }
                locals.add(meta);
            }

            //2、从数据库中查询出当前路径下的所有文件信息-视图2
            List<FileMeta> dbFiles = query(dir);
            //3、对此视图1和视图2
            //本地有，数据库没有的文件信息，进行插入操作
            //遍历本地文件，若数据库中不存在FileMeta就进行插入操作
            for (FileMeta meta :locals) {
                if (!dbFiles.contains(meta)){
                    save(meta);
                }
            }
            //数据库有的，本地没有的，做删除操作
            //遍历数据库文件，若本地文件中不存在FileMeta就进行删除操作
            for (FileMeta meta:dbFiles){
                if (!locals.contains(meta)){
                    delete(meta);
                }
            }
        }
    }

    /**
     * 删除数据库中的指定记录
     * @param meta
     */
    private void delete(FileMeta meta) {
      Connection connection=null;
      PreparedStatement ps=null;
      try{
          connection=DBUtil.getConnection();
          //通过sql删除文件和文件夹
          //此时删除的是文件
          String sql="delete from file_meta where"+
                  " (name = ? and path = ?)";
          if (meta.getIsDirectory()){
              //此处删除的是文件夹的第一级目录
              sql += " or path = ?";
              //此处删除的是多级子目录
              sql += " or path like ?";
          }
          ps=connection.prepareStatement(sql);
          ps.setString(1,meta.getName());
          ps.setString(2,meta.getPath());
          if (meta.getIsDirectory()){
              ps.setString(3,meta.getPath()+File.separator+meta.getName());
              ps.setString(4,meta.getPath()+File.separator+meta.getName()
              +File.separator+"%");
          }
          System.out.println("执行删除操作，SQL为:"+ps);
          int rows=ps.executeUpdate();
          if (meta.getIsDirectory()){
              System.out.println("删除文件夹"+meta.getName()+"成功，共删除"+rows+"文件");
          }else{
              System.out.println("删除文件"+meta.getName()+"成功");
          }
      }catch (SQLException e){
          System.out.println("文件删除出错，请检查sql语句");
          e.printStackTrace();
      }finally {
          DBUtil.close(connection,ps);
      }
    }

    /**
     * 将指定文件信息保存到数据库中
     * @param meta
     */
    private void save(FileMeta meta) {
        Connection connection=null;
        PreparedStatement ps=null;
        try{
            connection=DBUtil.getConnection();
            String sql="insert into file_meta values(?,?,?,?,?,?,?)";
            ps=connection.prepareStatement(sql);
            String fileName=meta.getName();
            ps.setString(1,fileName);
            ps.setString(2,meta.getPath());
            ps.setBoolean(3,meta.getIsDirectory());
            if (!meta.getIsDirectory()){
                //此处是文件，才会设置大小
                ps.setLong(4,meta.getSize());
            }
            ps.setTimestamp(5,new Timestamp(meta.getLastModified().getTime()));
            //到底是否需要存入拼音，就需要查看文件名是否包含中文
            if (PinyinUtil.containsChinese(fileName)){
                String[] pinyins=PinyinUtil.getPinyinByFileName(fileName);
                ps.setString(6,pinyins[0]);
                ps.setString(7,pinyins[1]);
            }
            System.out.println("执行文件的保存操作，SQL为:"+ps);
            int rows=ps.executeUpdate();
            System.out.println("成功保存"+rows+"行文件信息");
        }catch (SQLException e){
            System.out.println("保存文件信息出错，请检查SQL语句");
            e.printStackTrace();
        }finally {
            DBUtil.close(connection,ps);
        }
    }

    /**
     * 查询数据库中指定路径下的文件信息
     * @param dir
     * @return
     */
    private List<FileMeta> query(File dir) {
        Connection connection=null;
        PreparedStatement ps=null;
        ResultSet rs=null;
        List<FileMeta> dbFiles =new ArrayList<>();
        try{
            connection=DBUtil.getConnection();
            String sql="select name,path,is_directory,size,last_modified from file_meta"+
                    //注意在sql拼接时，换行需要加空格
                    " where path = ? ";
            ps=connection.prepareStatement(sql);
            ps.setString(1,dir.getPath());
            rs=ps.executeQuery();
            System.out.println("查询指定路径的SQL为:"+ps);
            while (rs.next()){
                FileMeta meta=new FileMeta();
                meta.setName(rs.getString("name"));
                meta.setPath(rs.getString("path"));
                meta.setIsDirectory(rs.getBoolean("is_directory"));
                meta.setLastModified(new Date(rs.getTimestamp("last_modified").getTime()));
                //只有是文件时才设置文件大小，如果是文件夹就应该设置为null
                //此处注意一个小问题，就是数据库中文件的size大小不为null，但是调用rs。getLong方法后若返回值为null，就会返回0
                if (!meta.getIsDirectory()){
                    //此处是文件
                    meta.setSize(rs.getLong("size"));
                }
                dbFiles.add(meta);
            }
        }catch (SQLException e){
            System.out.println("查询数据库指定路径下的文件出错，请检查SQL语句");
            e.printStackTrace();
        }finally {
            DBUtil.close(connection,ps,rs);
        }
        return dbFiles;
    }

    /**
     *
     * @param name
     * @param path
     * @param isDirectory
     * @param lastModified
     * @param meta
     */
    private void setCommonFiled(String name, String path, boolean isDirectory, long lastModified, FileMeta meta){
        meta.setName(name);
        meta.setPath(path);
        meta.setIsDirectory(isDirectory);
        //file对象的lastModified是一个long类型，以时间戳为单位
        meta.setLastModified(new Date(lastModified));
    }
}
